#pragma once
#include <vector>

namespace LearnCPlusPlus
{
	template<class T>
	class CCircularQueue
	{
	public:
		CCircularQueue(int vSize) { _ASSERTE(vSize >= 1); m_DataContainer.resize(vSize); }

		void reserve(int vSize)
		{
			std::vector<T> NewContainer(vSize);

			int NumData = 0;
			while (!empty() && NumData < vSize)	// vSize may be shorter than before
			{
				NewContainer[NumData++] = front();
				pop();
			}

			m_DataContainer = NewContainer;
			m_FrontPos = 0;
			m_BackPos = std::min(NumData, vSize) - 1;
			m_Empty = NewContainer.empty();
		}

		bool empty() const { return m_Empty; }
		int size() const { return empty() ? 0 : __circularPos(m_BackPos - m_FrontPos, m_DataContainer.size()) + 1; }

		void push(const T& vData)
		{
			if (!empty() && (__circularPos(m_FrontPos - m_BackPos, m_DataContainer.size()) == 1))
				return;
			if (m_DataContainer.size() == 1 && m_BackPos == m_FrontPos)
				return;
			else
			{
				m_BackPos = __circularPos(m_BackPos + 1, m_DataContainer.size());

				m_DataContainer[m_BackPos] = vData;
				m_Empty = false;
			}
		}

		void pop()
		{
			if (!empty())
			{
				m_DataContainer[m_FrontPos] = T{};	// for debug

				if (m_DataContainer.size() != 1)
				{
					m_FrontPos = __circularPos(m_FrontPos + 1, m_DataContainer.size());

					if (__circularPos(m_FrontPos - m_BackPos, m_DataContainer.size()) == 1)	// only pop will cause empty
						m_Empty = true;
				}
				else // size = 1 will get problem
				{
					m_BackPos = -1;	// not good
					m_Empty = true;
					return;
				}
			}
		}

		T& front() { return empty() ? m_DataContainer[0] : m_DataContainer[m_FrontPos]; }	// not good, prefer optional

		T& back() { return empty() ? m_DataContainer[0] : m_DataContainer[m_BackPos]; }	// not good

	private:
		int __circularPos(int vPos, std::size_t vSize) const { return int((vPos + vSize) % vSize); }

		std::vector<T> m_DataContainer;

		int m_FrontPos = 0, m_BackPos = -1;

		bool m_Empty = true;
	};
}
